﻿using System;
using System.Collections.Generic;
using System.IdentityModel.Claims;
using System.Linq;
using System.Web;

namespace Altairis.IdentityToolkit.LiveId {
    /// <summary>
    /// Class representing the <see cref="IdentityLinkProvider" /> for Windows Live ID.
    /// </summary>
    public abstract class LiveIdIdentityLinkProvider : IdentityLinkProvider {

        /// <summary>
        /// Checks if given provider supports certain type of claims.
        /// </summary>
        /// <param name="claimSet">The claim set to be analyzed.</param>
        /// <returns><c>true</c> if provider supports given claim set; otherwise <c>false</c>.</returns>
        public override bool Supports(ClaimSet claimSet) {
            if (claimSet == null) return false;
            IEnumerable<Claim> claimList = claimSet.FindClaims(LiveClaimTypes.UserUniqueId, Rights.Identity);
            return claimList != null && claimList.Count() != 0;
        }

        /// <summary>
        /// Resolves the link between given claim set and user name.
        /// </summary>
        /// <param name="claimSet">The claim set.</param>
        /// <returns>membership user name linked to given claim set. Returns <c>null</c> when no link was found.</returns>
        public override string ResolveLink(ClaimSet claimSet) {
            // Validate arguments
            if (claimSet == null) throw new ArgumentNullException("claims");

            // Call internal method
            return this.InternalResolveLink(GetUserUniqueId(claimSet));
        }

        /// <summary>
        /// Creates the link between given claim set and user name.
        /// </summary>
        /// <param name="claimSet">The claim set.</param>
        /// <param name="userName">Membership user name.</param>
        public override void CreateLink(ClaimSet claimSet, string userName) {
            // Validate arguments
            if (claimSet == null) throw new ArgumentNullException("claims");
            if (userName == null) throw new ArgumentNullException("userName");
            if (string.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty string.", "userName");

            // Call internal method
            this.InternalCreateLink(GetUserUniqueId(claimSet), userName);
        }

        /// <summary>
        /// Extracts the user unique id from given <see cref="System.IdentityModel.Claims.ClaimSet"/>.
        /// </summary>
        /// <param name="claimSet">The claim set.</param>
        /// <returns>unique user ID.</returns>
        protected static Guid GetUserUniqueId(ClaimSet claimSet) {
            if (claimSet == null) throw new ArgumentNullException("claims");

            // Find unique user id
            IEnumerable<Claim> claimList = claimSet.FindClaims(LiveClaimTypes.UserUniqueId, Rights.Identity);
            if (claimList == null || claimList.Count() == 0) throw new NotSupportedException("Supplied ClaimSet does not contain expected claim '" + LiveClaimTypes.UserUniqueId + "'.");

            // Get it as GUID
            return (Guid)(claimList.First().Resource);
        }

        /// <summary>
        /// Internal method for link resolving.
        /// </summary>
        /// <param name="uid">The unique user ID.</param>
        /// <returns>user name of linked user or <c>null</c> when no linked user is found.</returns>
        protected abstract string InternalResolveLink(Guid uid);

        /// <summary>
        /// Internal method for creating link between unique user IDs and user names.
        /// </summary>
        /// <param name="uid">The unique user ID.</param>
        /// <param name="userName">User name.</param>
        protected abstract void InternalCreateLink(Guid uid, string userName);

        /// <summary>
        /// Removes the link between unique user IDs and user names.
        /// </summary>
        /// <param name="uid">The unique user ID.</param>
        public abstract void RemoveLink(Guid uid);

        /// <summary>
        /// Removes the link between unique user IDs and user names.
        /// </summary>
        /// <param name="uid">The unique user ID.</param>
        /// <param name="userName">User name.</param>
        public abstract void RemoveLink(Guid uid, string userName);

        /// <summary>
        /// Gets the identity links for current user.
        /// </summary>
        /// <returns>List of <see cref="LiveIdIdentityLink"/>s.</returns>
        public IEnumerable<LiveIdIdentityLink> GetLinksForUser() {
            HttpContext ctx = System.Web.HttpContext.Current;
            if (ctx == null) throw new NotSupportedException("This overload is supported only in HTTP context.");
            if (!ctx.Request.IsAuthenticated) throw new NotSupportedException("This overload is supported only for authenticated users.");
            return GetLinksForUser(ctx.User.Identity.Name);
        }

        /// <summary>
        /// Gets the identity links for given user.
        /// </summary>
        /// <param name="userName">User name.</param>
        /// <returns>List of <see cref="LiveIdIdentityLink"/>s.</returns>
        public abstract IEnumerable<LiveIdIdentityLink> GetLinksForUser(string userName);

    }

    /// <summary>
    /// Class representing link between membership user and Live ID Unique user IDs.
    /// </summary>
    public class LiveIdIdentityLink {

        /// <summary>
        /// Gets or sets the unique user ID.
        /// </summary>
        /// <value>The unique user ID.</value>
        public Guid UniqueUserId { get; set; }

        /// <summary>
        /// Gets or sets the user name.
        /// </summary>
        /// <value>The user name.</value>
        public string UserName { get; set; }

        /// <summary>
        /// Gets or sets the date when unique ID was assigned to given user.
        /// </summary>
        /// <value>The date when unique ID was assigned to given user.</value>
        public DateTime DateAssigned { get; set; }
    
    }

}
